/*
 * Copyright 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef VECMATH_H_
#define VECMATH_H_

#include <cmath>
#include "JNIHelper.h"

namespace ndk_helper {

/******************************************************************
 * Helper class for vector math operations
 * Currently all implementations are in pure C++.
 * Each class is an opaque class so caller does not have a direct access
 * to each element. This is for an ease of future optimization to use vector
 *operations.
 *
 */

class Vec2;
class Vec3;
class Vec4;
class Mat4;

/******************************************************************
 * 2 elements vector class
 *
 */
class Vec2 {
 public:
  float x;
  float y;
  friend class Vec3;
  friend class Vec4;
  friend class Mat4;
  friend class Quaternion;

  Vec2() { x = y = 0.f; }

  Vec2(const float fX, const float fY) {
    x = fX;
    y = fY;
  }

  Vec2(const Vec2& vec) {
    x = vec.x;
    y = vec.y;
  }

  Vec2(const float* pVec) {
    x = (*pVec++);
    y = (*pVec++);
  }

  // Operators
  Vec2 operator*(const Vec2& rhs) const {
    Vec2 ret;
    ret.x = x * rhs.x;
    ret.y = y * rhs.y;
    return ret;
  }

  Vec2 operator/(const Vec2& rhs) const {
    Vec2 ret;
    ret.x = x / rhs.x;
    ret.y = y / rhs.y;
    return ret;
  }

  Vec2 operator+(const Vec2& rhs) const {
    Vec2 ret;
    ret.x = x + rhs.x;
    ret.y = y + rhs.y;
    return ret;
  }

  Vec2 operator-(const Vec2& rhs) const {
    Vec2 ret;
    ret.x = x - rhs.x;
    ret.y = y - rhs.y;
    return ret;
  }

  Vec2& operator+=(const Vec2& rhs) {
    x += rhs.x;
    y += rhs.y;
    return *this;
  }

  Vec2& operator-=(const Vec2& rhs) {
    x -= rhs.x;
    y -= rhs.y;
    return *this;
  }

  Vec2& operator*=(const Vec2& rhs) {
    x *= rhs.x;
    y *= rhs.y;
    return *this;
  }

  Vec2& operator/=(const Vec2& rhs) {
    x /= rhs.x;
    y /= rhs.y;
    return *this;
  }

  // External operators
  friend Vec2 operator-(const Vec2& rhs) { return Vec2(rhs) *= -1; }

  friend Vec2 operator*(const float lhs, const Vec2& rhs) {
    Vec2 ret;
    ret.x = lhs * rhs.x;
    ret.y = lhs * rhs.y;
    return ret;
  }

  friend Vec2 operator/(const float lhs, const Vec2& rhs) {
    Vec2 ret;
    ret.x = lhs / rhs.x;
    ret.y = lhs / rhs.y;
    return ret;
  }

  // Operators with float
  Vec2 operator*(const float& rhs) const {
    Vec2 ret;
    ret.x = x * rhs;
    ret.y = y * rhs;
    return ret;
  }

  Vec2& operator*=(const float& rhs) {
    x = x * rhs;
    y = y * rhs;
    return *this;
  }

  Vec2 operator/(const float& rhs) const {
    Vec2 ret;
    ret.x = x / rhs;
    ret.y = y / rhs;
    return ret;
  }

  Vec2& operator/=(const float& rhs) {
    x = x / rhs;
    y = y / rhs;
    return *this;
  }

  // Compare
  bool operator==(const Vec2& rhs) const {
    if (x != rhs.x || y != rhs.y) return false;
    return true;
  }

  bool operator!=(const Vec2& rhs) const {
    if (x == rhs.x) return false;

    return true;
  }

  float Length() const { return sqrtf(x * x + y * y); }

  Vec2 Normalize() {
    float len = Length();
    x = x / len;
    y = y / len;
    return *this;
  }

  float Dot(const Vec2& rhs) { return x * rhs.x + y * rhs.y; }

  bool Validate() {
    if (std::isnan(x) || std::isnan(y)) return false;
    return true;
  }

  void Value(float& fX, float& fY) {
    fX = x;
    fY = y;
  }

  void Dump() { LOGI("Vec2 %f %f", x, y); }
};

/******************************************************************
 * 3 elements vector class
 *
 */
class Vec3 {
 private:
  float x, y, z;

 public:
  friend class Vec4;
  friend class Mat4;
  friend class Quaternion;

  Vec3() { x = y = z = 0.f; }

  Vec3(const float fX, const float fY, const float fZ) {
    x = fX;
    y = fY;
    z = fZ;
  }

  Vec3(const Vec3& vec) {
    x = vec.x;
    y = vec.y;
    z = vec.z;
  }

  Vec3(const float* pVec) {
    x = (*pVec++);
    y = (*pVec++);
    z = *pVec;
  }

  Vec3(const Vec2& vec, float f) {
    x = vec.x;
    y = vec.y;
    z = f;
  }

  Vec3(const Vec4& vec);

  // Operators
  Vec3 operator*(const Vec3& rhs) const {
    Vec3 ret;
    ret.x = x * rhs.x;
    ret.y = y * rhs.y;
    ret.z = z * rhs.z;
    return ret;
  }

  Vec3 operator/(const Vec3& rhs) const {
    Vec3 ret;
    ret.x = x / rhs.x;
    ret.y = y / rhs.y;
    ret.z = z / rhs.z;
    return ret;
  }

  Vec3 operator+(const Vec3& rhs) const {
    Vec3 ret;
    ret.x = x + rhs.x;
    ret.y = y + rhs.y;
    ret.z = z + rhs.z;
    return ret;
  }

  Vec3 operator-(const Vec3& rhs) const {
    Vec3 ret;
    ret.x = x - rhs.x;
    ret.y = y - rhs.y;
    ret.z = z - rhs.z;
    return ret;
  }

  Vec3& operator+=(const Vec3& rhs) {
    x += rhs.x;
    y += rhs.y;
    z += rhs.z;
    return *this;
  }

  Vec3& operator-=(const Vec3& rhs) {
    x -= rhs.x;
    y -= rhs.y;
    z -= rhs.z;
    return *this;
  }

  Vec3& operator*=(const Vec3& rhs) {
    x *= rhs.x;
    y *= rhs.y;
    z *= rhs.z;
    return *this;
  }

  Vec3& operator/=(const Vec3& rhs) {
    x /= rhs.x;
    y /= rhs.y;
    z /= rhs.z;
    return *this;
  }

  // External operators
  friend Vec3 operator-(const Vec3& rhs) { return Vec3(rhs) *= -1; }

  friend Vec3 operator*(const float lhs, const Vec3& rhs) {
    Vec3 ret;
    ret.x = lhs * rhs.x;
    ret.y = lhs * rhs.y;
    ret.z = lhs * rhs.z;
    return ret;
  }

  friend Vec3 operator/(const float lhs, const Vec3& rhs) {
    Vec3 ret;
    ret.x = lhs / rhs.x;
    ret.y = lhs / rhs.y;
    ret.z = lhs / rhs.z;
    return ret;
  }

  // Operators with float
  Vec3 operator*(const float& rhs) const {
    Vec3 ret;
    ret.x = x * rhs;
    ret.y = y * rhs;
    ret.z = z * rhs;
    return ret;
  }

  Vec3& operator*=(const float& rhs) {
    x = x * rhs;
    y = y * rhs;
    z = z * rhs;
    return *this;
  }

  Vec3 operator/(const float& rhs) const {
    Vec3 ret;
    ret.x = x / rhs;
    ret.y = y / rhs;
    ret.z = z / rhs;
    return ret;
  }

  Vec3& operator/=(const float& rhs) {
    x = x / rhs;
    y = y / rhs;
    z = z / rhs;
    return *this;
  }

  // Compare
  bool operator==(const Vec3& rhs) const {
    if (x != rhs.x || y != rhs.y || z != rhs.z) return false;
    return true;
  }

  bool operator!=(const Vec3& rhs) const {
    if (x == rhs.x) return false;

    return true;
  }

  float Length() const { return sqrtf(x * x + y * y + z * z); }

  Vec3 Normalize() {
    float len = Length();
    x = x / len;
    y = y / len;
    z = z / len;
    return *this;
  }

  float Dot(const Vec3& rhs) { return x * rhs.x + y * rhs.y + z * rhs.z; }

  Vec3 Cross(const Vec3& rhs) {
    Vec3 ret;
    ret.x = y * rhs.z - z * rhs.y;
    ret.y = z * rhs.x - x * rhs.z;
    ret.z = x * rhs.y - y * rhs.x;
    return ret;
  }

  bool Validate() {
    if (std::isnan(x) || std::isnan(y) || std::isnan(z)) return false;
    return true;
  }

  void Value(float& fX, float& fY, float& fZ) {
    fX = x;
    fY = y;
    fZ = z;
  }

  void Dump() { LOGI("Vec3 %f %f %f", x, y, z); }
};

/******************************************************************
 * 4 elements vector class
 *
 */
class Vec4 {
 private:
  float x, y, z, w;

 public:
  friend class Vec3;
  friend class Mat4;
  friend class Quaternion;

  Vec4() { x = y = z = w = 0.f; }

  Vec4(const float fX, const float fY, const float fZ, const float fW) {
    x = fX;
    y = fY;
    z = fZ;
    w = fW;
  }

  Vec4(const Vec4& vec) {
    x = vec.x;
    y = vec.y;
    z = vec.z;
    w = vec.w;
  }

  Vec4(const Vec3& vec, const float fW) {
    x = vec.x;
    y = vec.y;
    z = vec.z;
    w = fW;
  }

  Vec4(const float* pVec) {
    x = (*pVec++);
    y = (*pVec++);
    z = *pVec;
    w = *pVec;
  }

  // Operators
  Vec4 operator*(const Vec4& rhs) const {
    Vec4 ret;
    ret.x = x * rhs.x;
    ret.y = y * rhs.y;
    ret.z = z * rhs.z;
    ret.w = z * rhs.w;
    return ret;
  }

  Vec4 operator/(const Vec4& rhs) const {
    Vec4 ret;
    ret.x = x / rhs.x;
    ret.y = y / rhs.y;
    ret.z = z / rhs.z;
    ret.w = z / rhs.w;
    return ret;
  }

  Vec4 operator+(const Vec4& rhs) const {
    Vec4 ret;
    ret.x = x + rhs.x;
    ret.y = y + rhs.y;
    ret.z = z + rhs.z;
    ret.w = z + rhs.w;
    return ret;
  }

  Vec4 operator-(const Vec4& rhs) const {
    Vec4 ret;
    ret.x = x - rhs.x;
    ret.y = y - rhs.y;
    ret.z = z - rhs.z;
    ret.w = z - rhs.w;
    return ret;
  }

  Vec4& operator+=(const Vec4& rhs) {
    x += rhs.x;
    y += rhs.y;
    z += rhs.z;
    w += rhs.w;
    return *this;
  }

  Vec4& operator-=(const Vec4& rhs) {
    x -= rhs.x;
    y -= rhs.y;
    z -= rhs.z;
    w -= rhs.w;
    return *this;
  }

  Vec4& operator*=(const Vec4& rhs) {
    x *= rhs.x;
    y *= rhs.y;
    z *= rhs.z;
    w *= rhs.w;
    return *this;
  }

  Vec4& operator/=(const Vec4& rhs) {
    x /= rhs.x;
    y /= rhs.y;
    z /= rhs.z;
    w /= rhs.w;
    return *this;
  }

  // External operators
  friend Vec4 operator-(const Vec4& rhs) { return Vec4(rhs) *= -1; }

  friend Vec4 operator*(const float lhs, const Vec4& rhs) {
    Vec4 ret;
    ret.x = lhs * rhs.x;
    ret.y = lhs * rhs.y;
    ret.z = lhs * rhs.z;
    ret.w = lhs * rhs.w;
    return ret;
  }

  friend Vec4 operator/(const float lhs, const Vec4& rhs) {
    Vec4 ret;
    ret.x = lhs / rhs.x;
    ret.y = lhs / rhs.y;
    ret.z = lhs / rhs.z;
    ret.w = lhs / rhs.w;
    return ret;
  }

  // Operators with float
  Vec4 operator*(const float& rhs) const {
    Vec4 ret;
    ret.x = x * rhs;
    ret.y = y * rhs;
    ret.z = z * rhs;
    ret.w = w * rhs;
    return ret;
  }

  Vec4& operator*=(const float& rhs) {
    x = x * rhs;
    y = y * rhs;
    z = z * rhs;
    w = w * rhs;
    return *this;
  }

  Vec4 operator/(const float& rhs) const {
    Vec4 ret;
    ret.x = x / rhs;
    ret.y = y / rhs;
    ret.z = z / rhs;
    ret.w = w / rhs;
    return ret;
  }

  Vec4& operator/=(const float& rhs) {
    x = x / rhs;
    y = y / rhs;
    z = z / rhs;
    w = w / rhs;
    return *this;
  }

  // Compare
  bool operator==(const Vec4& rhs) const {
    if (x != rhs.x || y != rhs.y || z != rhs.z || w != rhs.w)
      return false;
    return true;
  }

  bool operator!=(const Vec4& rhs) const {
    if (x == rhs.x) return false;

    return true;
  }

  Vec4 operator*(const Mat4& rhs) const;

  float Length() const { return sqrtf(x * x + y * y + z * z + w * w); }

  Vec4 Normalize() {
    float len = Length();
    x = x / len;
    y = y / len;
    z = z / len;
    w = w / len;
    return *this;
  }

  float Dot(const Vec3& rhs) { return x * rhs.x + y * rhs.y + z * rhs.z; }

  Vec3 Cross(const Vec3& rhs) {
    Vec3 ret;
    ret.x = y * rhs.z - z * rhs.y;
    ret.y = z * rhs.x - x * rhs.z;
    ret.z = x * rhs.y - y * rhs.x;
    return ret;
  }

  bool Validate() {
    if (std::isnan(x) || std::isnan(y) ||
        std::isnan(z) || std::isnan(w))
      return false;

    return true;
  }

  void Value(float& fX, float& fY, float& fZ, float& fW) {
    fX = x;
    fY = y;
    fZ = z;
    fW = w;
  }
};

/******************************************************************
 * 4x4 matrix
 *
 */
class Mat4 {
 private:
  float f_[16];

 public:
  friend class Vec3;
  friend class Vec4;
  friend class Quaternion;

  Mat4();
  Mat4(const float*);

  Mat4 operator*(const Mat4& rhs) const;
  Vec4 operator*(const Vec4& rhs) const;

  Mat4 operator+(const Mat4& rhs) const {
    Mat4 ret;
    for (int32_t i = 0; i < 16; ++i) {
      ret.f_[i] = f_[i] + rhs.f_[i];
    }
    return ret;
  }

  Mat4 operator-(const Mat4& rhs) const {
    Mat4 ret;
    for (int32_t i = 0; i < 16; ++i) {
      ret.f_[i] = f_[i] - rhs.f_[i];
    }
    return ret;
  }

  Mat4& operator+=(const Mat4& rhs) {
    for (int32_t i = 0; i < 16; ++i) {
      f_[i] += rhs.f_[i];
    }
    return *this;
  }

  Mat4& operator-=(const Mat4& rhs) {
    for (int32_t i = 0; i < 16; ++i) {
      f_[i] -= rhs.f_[i];
    }
    return *this;
  }

  Mat4& operator*=(const Mat4& rhs) {
    Mat4 ret;
    ret.f_[0] = f_[0] * rhs.f_[0] + f_[4] * rhs.f_[1] + f_[8] * rhs.f_[2] +
                f_[12] * rhs.f_[3];
    ret.f_[1] = f_[1] * rhs.f_[0] + f_[5] * rhs.f_[1] + f_[9] * rhs.f_[2] +
                f_[13] * rhs.f_[3];
    ret.f_[2] = f_[2] * rhs.f_[0] + f_[6] * rhs.f_[1] + f_[10] * rhs.f_[2] +
                f_[14] * rhs.f_[3];
    ret.f_[3] = f_[3] * rhs.f_[0] + f_[7] * rhs.f_[1] + f_[11] * rhs.f_[2] +
                f_[15] * rhs.f_[3];

    ret.f_[4] = f_[0] * rhs.f_[4] + f_[4] * rhs.f_[5] + f_[8] * rhs.f_[6] +
                f_[12] * rhs.f_[7];
    ret.f_[5] = f_[1] * rhs.f_[4] + f_[5] * rhs.f_[5] + f_[9] * rhs.f_[6] +
                f_[13] * rhs.f_[7];
    ret.f_[6] = f_[2] * rhs.f_[4] + f_[6] * rhs.f_[5] + f_[10] * rhs.f_[6] +
                f_[14] * rhs.f_[7];
    ret.f_[7] = f_[3] * rhs.f_[4] + f_[7] * rhs.f_[5] + f_[11] * rhs.f_[6] +
                f_[15] * rhs.f_[7];

    ret.f_[8] = f_[0] * rhs.f_[8] + f_[4] * rhs.f_[9] + f_[8] * rhs.f_[10] +
                f_[12] * rhs.f_[11];
    ret.f_[9] = f_[1] * rhs.f_[8] + f_[5] * rhs.f_[9] + f_[9] * rhs.f_[10] +
                f_[13] * rhs.f_[11];
    ret.f_[10] = f_[2] * rhs.f_[8] + f_[6] * rhs.f_[9] + f_[10] * rhs.f_[10] +
                 f_[14] * rhs.f_[11];
    ret.f_[11] = f_[3] * rhs.f_[8] + f_[7] * rhs.f_[9] + f_[11] * rhs.f_[10] +
                 f_[15] * rhs.f_[11];

    ret.f_[12] = f_[0] * rhs.f_[12] + f_[4] * rhs.f_[13] + f_[8] * rhs.f_[14] +
                 f_[12] * rhs.f_[15];
    ret.f_[13] = f_[1] * rhs.f_[12] + f_[5] * rhs.f_[13] + f_[9] * rhs.f_[14] +
                 f_[13] * rhs.f_[15];
    ret.f_[14] = f_[2] * rhs.f_[12] + f_[6] * rhs.f_[13] + f_[10] * rhs.f_[14] +
                 f_[14] * rhs.f_[15];
    ret.f_[15] = f_[3] * rhs.f_[12] + f_[7] * rhs.f_[13] + f_[11] * rhs.f_[14] +
                 f_[15] * rhs.f_[15];

    *this = ret;
    return *this;
  }

  Mat4 operator*(const float rhs) {
    Mat4 ret;
    for (int32_t i = 0; i < 16; ++i) {
      ret.f_[i] = f_[i] * rhs;
    }
    return ret;
  }

  Mat4& operator*=(const float rhs) {
    for (int32_t i = 0; i < 16; ++i) {
      f_[i] *= rhs;
    }
    return *this;
  }

  Mat4& operator=(const Mat4& rhs) {
    for (int32_t i = 0; i < 16; ++i) {
      f_[i] = rhs.f_[i];
    }
    return *this;
  }

  Mat4 Inverse();

  Mat4 Transpose() {
    Mat4 ret;
    ret.f_[0] = f_[0];
    ret.f_[1] = f_[4];
    ret.f_[2] = f_[8];
    ret.f_[3] = f_[12];
    ret.f_[4] = f_[1];
    ret.f_[5] = f_[5];
    ret.f_[6] = f_[9];
    ret.f_[7] = f_[13];
    ret.f_[8] = f_[2];
    ret.f_[9] = f_[6];
    ret.f_[10] = f_[10];
    ret.f_[11] = f_[14];
    ret.f_[12] = f_[3];
    ret.f_[13] = f_[7];
    ret.f_[14] = f_[11];
    ret.f_[15] = f_[15];
    *this = ret;
    return *this;
  }

  Mat4& PostTranslate(float tx, float ty, float tz) {
    f_[12] += (tx * f_[0]) + (ty * f_[4]) + (tz * f_[8]);
    f_[13] += (tx * f_[1]) + (ty * f_[5]) + (tz * f_[9]);
    f_[14] += (tx * f_[2]) + (ty * f_[6]) + (tz * f_[10]);
    f_[15] += (tx * f_[3]) + (ty * f_[7]) + (tz * f_[11]);
    return *this;
  }

  float* Ptr() { return f_; }

  //--------------------------------------------------------------------------------
  // Misc
  //--------------------------------------------------------------------------------
  static Mat4 Perspective(float width, float height, float nearPlane,
                          float farPlane);
  static Mat4 Ortho2D(float left, float top, float right, float bottom);

  static Mat4 LookAt(const Vec3& vEye, const Vec3& vAt, const Vec3& vUp);

  static Mat4 Translation(const float fX, const float fY, const float fZ);
  static Mat4 Translation(const Vec3 vec);

  static Mat4 RotationX(const float angle);

  static Mat4 RotationY(const float angle);

  static Mat4 RotationZ(const float angle);

  static Mat4 Scale(const float scaleX, const float scaleY, const float scaleZ);

  static Mat4 Identity() {
    Mat4 ret;
    ret.f_[0] = 1.f;
    ret.f_[1] = 0;
    ret.f_[2] = 0;
    ret.f_[3] = 0;
    ret.f_[4] = 0;
    ret.f_[5] = 1.f;
    ret.f_[6] = 0;
    ret.f_[7] = 0;
    ret.f_[8] = 0;
    ret.f_[9] = 0;
    ret.f_[10] = 1.f;
    ret.f_[11] = 0;
    ret.f_[12] = 0;
    ret.f_[13] = 0;
    ret.f_[14] = 0;
    ret.f_[15] = 1.f;
    return ret;
  }

  void Dump() {
    LOGI("%f %f %f %f", f_[0], f_[1], f_[2], f_[3]);
    LOGI("%f %f %f %f", f_[4], f_[5], f_[6], f_[7]);
    LOGI("%f %f %f %f", f_[8], f_[9], f_[10], f_[11]);
    LOGI("%f %f %f %f", f_[12], f_[13], f_[14], f_[15]);
  }
};

/******************************************************************
 * Quaternion class
 *
 */
class Quaternion {
 private:
  float x, y, z, w;

 public:
  friend class Vec3;
  friend class Vec4;
  friend class Mat4;

  Quaternion() {
    x = 0.f;
    y = 0.f;
    z = 0.f;
    w = 1.f;
  }

  Quaternion(const float fX, const float fY, const float fZ, const float fW) {
    x = fX;
    y = fY;
    z = fZ;
    w = fW;
  }

  Quaternion(const Vec3 vec, const float fW) {
    x = vec.x;
    y = vec.y;
    z = vec.z;
    w = fW;
  }

  Quaternion(const float* p) {
    x = *p++;
    y = *p++;
    z = *p++;
    w = *p++;
  }

  Quaternion operator*(const Quaternion rhs) {
    Quaternion ret;
    ret.x = x * rhs.w + y * rhs.z - z * rhs.y + w * rhs.x;
    ret.y = -x * rhs.z + y * rhs.w + z * rhs.x + w * rhs.y;
    ret.z = x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z;
    ret.w = -x * rhs.x - y * rhs.y - z * rhs.z + w * rhs.w;
    return ret;
  }

  Quaternion& operator*=(const Quaternion rhs) {
    Quaternion ret;
    ret.x = x * rhs.w + y * rhs.z - z * rhs.y + w * rhs.x;
    ret.y = -x * rhs.z + y * rhs.w + z * rhs.x + w * rhs.y;
    ret.z = x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z;
    ret.w = -x * rhs.x - y * rhs.y - z * rhs.z + w * rhs.w;
    *this = ret;
    return *this;
  }

  Quaternion Conjugate() {
    x = -x;
    y = -y;
    z = -z;
    return *this;
  }

  // Non destuctive version
  Quaternion Conjugated() {
    Quaternion ret;
    ret.x = -x;
    ret.y = -y;
    ret.z = -z;
    ret.w = w;
    return ret;
  }

  void ToMatrix(Mat4& mat) {
    float x2 = x * x * 2.0f;
    float y2 = y * y * 2.0f;
    float z2 = z * z * 2.0f;
    float xy = x * y * 2.0f;
    float yz = y * z * 2.0f;
    float zx = z * x * 2.0f;
    float xw = x * w * 2.0f;
    float yw = y * w * 2.0f;
    float zw = z * w * 2.0f;

    mat.f_[0] = 1.0f - y2 - z2;
    mat.f_[1] = xy + zw;
    mat.f_[2] = zx - yw;
    mat.f_[4] = xy - zw;
    mat.f_[5] = 1.0f - z2 - x2;
    mat.f_[6] = yz + xw;
    mat.f_[8] = zx + yw;
    mat.f_[9] = yz - xw;
    mat.f_[10] = 1.0f - x2 - y2;

    mat.f_[3] = mat.f_[7] = mat.f_[11] = mat.f_[12] = mat.f_[13] = mat.f_[14] =
        0.0f;
    mat.f_[15] = 1.0f;
  }

  void ToMatrixPreserveTranslate(Mat4& mat) {
    float x2 = x * x * 2.0f;
    float y2 = y * y * 2.0f;
    float z2 = z * z * 2.0f;
    float xy = x * y * 2.0f;
    float yz = y * z * 2.0f;
    float zx = z * x * 2.0f;
    float xw = x * w * 2.0f;
    float yw = y * w * 2.0f;
    float zw = z * w * 2.0f;

    mat.f_[0] = 1.0f - y2 - z2;
    mat.f_[1] = xy + zw;
    mat.f_[2] = zx - yw;
    mat.f_[4] = xy - zw;
    mat.f_[5] = 1.0f - z2 - x2;
    mat.f_[6] = yz + xw;
    mat.f_[8] = zx + yw;
    mat.f_[9] = yz - xw;
    mat.f_[10] = 1.0f - x2 - y2;

    mat.f_[3] = mat.f_[7] = mat.f_[11] = 0.0f;
    mat.f_[15] = 1.0f;
  }

  static Quaternion RotationAxis(const Vec3 axis, const float angle) {
    Quaternion ret;
    float s = sinf(angle / 2);
    ret.x = s * axis.x;
    ret.y = s * axis.y;
    ret.z = s * axis.z;
    ret.w = cosf(angle / 2);
    return ret;
  }

  void Value(float& fX, float& fY, float& fZ, float& fW) {
    fX = x;
    fY = y;
    fZ = z;
    fW = w;
  }
};

}  // namespace ndk_helper
#endif /* VECMATH_H_ */
